# -*- coding: utf-8 -*-
"""
    xfree.core.auth.tokens
    ~~~~~~~~~~~~~~~~~~~~~
    文件描述：本模块提供了与xfree中的令牌交互的方法。
    作者：xfree
    邮箱：cy_83k@qq.com
"""

from abc import ABC, abstractmethod

import attr
from flask_babelplus import gettext as _

from .exceptions import BaseXFreeError


class TokenError(BaseXFreeError):
    """
    当反序列化令牌出现问题时抛出的异常。
    包含有帮助的类方法以确保一致的措辞。

    :param str reason: 解释令牌为何无效的原因
    """

    def __init__(self, reason):
        self.reason = reason  # 令牌无效的原因
        super(TokenError, self).__init__(reason)

    @classmethod
    def invalid(cls):
        """
        用于抛出由于签名不正确、被篡改、无法解析或包含不适当的操作而无效的令牌的异常。
        """
        return cls(_("Token is invalid"))

    @classmethod
    def expired(cls):
        """
        用于抛出因过期而不再可用的令牌的异常。
        """
        return cls(_("Token is expired"))

    # 理论上这永远不会被抛出
    # 但它是为了在处理过程完全出错时的通用捕获而提供的
    @classmethod  # pragma: no cover
    def bad(cls):
        return cls(_("Token cannot be processed"))


# 令牌操作的持有者
# 不是枚举，所以插件可以添加到它
class TokenActions:
    """
    令牌操作的集合。

    .. note::
        这只是一个类而不是枚举，因为枚举不能在运行时扩展，
        这将限制xfree本身实现的令牌操作数量，并阻止插件扩展令牌。
    """

    RESET_PASSWORD = "reset_password"  # 重置密码操作
    ACTIVATE_ACCOUNT = "activate_account"  # 激活账户操作


@attr.s(frozen=True, eq=True, order=True, hash=True)
class Token(object):
    """
    :param int user_id: 用户的唯一标识符
    :param str operation: 从TokenActions类中选择的操作
    """

    user_id = attr.ib()  # 用户ID
    operation = attr.ib()  # 操作


class TokenSerializer(ABC):
    """ """

    @abstractmethod
    def dumps(self, token):
        """
        此方法是抽象的。

        用于将令牌转换为它的字符串表示。

        :param token: 要转换的令牌
        :type token: :class:`Token<xfree.core.tokens.Token>`
        :returns str: 令牌的字符串表示
        """
        pass

    @abstractmethod
    def loads(self, raw_token):
        """
        此方法是抽象的

        用于将令位的字符串表示转换为实际的
        :class:`Token<xfree.core.tokens.Token>` 实例

        :param str raw_token: 令牌的字符串表示
        :returns token: 解析后的令牌
        :rtype: :class:`Token<xfree.core.tokens.Token>`
        """
        pass


class TokenVerifier(ABC):
    """
    用于在反序列化后验证令牌的有效性，
    例如电子邮件是否与提供的令牌中的用户ID匹配。

    如果验证失败，应该抛出
    :class:`ValidationError<flask基类.exceptions.ValidationError>`
    异常。
    """

    @abstractmethod
    def verify_token(self, token, **kwargs):
        """
        此方法是抽象的。

        :param token: 要验证的解析后的令牌
        :param kwargs: 验证令牌的任意上下文
        :type token: :class:`Token<xfree.core.tokens.Token>`
        """
        pass

    def __call__(self, token, **kwargs):
        return self.verify_token(token, **kwargs)  # 允许实例像函数一样被调用